package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumAttributeValueService;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.album.service.AuditService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.atguigu.tingshu.common.constant.SystemConstant.*;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private AlbumAttributeValueMapper albumAttributeValueMapper;

	@Autowired
	private AlbumStatMapper albumStatMapper;

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private AuditService auditService;

	@Autowired
	private AlbumAttributeValueService albumAttributeValueService;

	@Autowired
	private RabbitService rabbitService;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private RedissonClient redissonClient;

	@Autowired
	private UserFeignClient userFeignClient;

	/**
	 * 内容创作者或者平台运营人员-保存专辑
	 *
	 * @param albumInfoVo
	 * @param userId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)//默认RuntimeException跟Error回滚事务
	public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {
		//1.保存专辑信息
		//1.1 将专辑VO转为PO对象
		AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);

		//1.2 为专辑属性赋值：用户ID，试听集数，审核状态
		albumInfo.setUserId(userId);
		String payType = albumInfo.getPayType();
		if (ALBUM_PAY_TYPE_VIPFREE.equals(payType) || ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
			//只需要对VIP免费或付费资源设置试听集
			albumInfo.setTracksForFree(3);
		}
		albumInfo.setStatus(ALBUM_STATUS_NO_PASS);

		//1.3 保存专辑，得到专辑ID
		albumInfoMapper.insert(albumInfo);
		Long albumId = albumInfo.getId();

		//2.保存专辑标签关系信息
		//2.1 获取VO中提交专辑标签关系集合
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
		if (CollUtil.isNotEmpty(albumAttributeValueVoList)) {
			//2.2 为专辑标签关联专辑ID，"批量"新增专辑标签关系
			for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
				AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
				albumAttributeValue.setAlbumId(albumId);
				albumAttributeValueMapper.insert(albumAttributeValue);
			}
		}

		//3.初始化专辑统计信息
		this.saveAlbumInfoStat(albumId, ALBUM_STAT_PLAY, 0);
		this.saveAlbumInfoStat(albumId, ALBUM_STAT_SUBSCRIBE, 0);
		this.saveAlbumInfoStat(albumId, ALBUM_STAT_BUY, 0);
		this.saveAlbumInfoStat(albumId, ALBUM_STAT_COMMENT, 0);

		//4.对专辑中文本内容进行审核  & 索引库ES中新增记录
		String text = albumInfo.getAlbumTitle() + albumInfo.getAlbumIntro();
		String suggestion = auditService.auditText(text);
		if ("pass".equals(suggestion)) {
			albumInfo.setStatus(ALBUM_STATUS_PASS);
			//5.内容审核通过后发送MQ消息完成专辑上架（存入ES）
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumId);
		} else if ("review".equals(suggestion)) {
			albumInfo.setStatus(ALBUM_STATUS_REVIEW);
		} else if ("block".equals(suggestion)) {
			albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
		}
		albumInfoMapper.updateById(albumInfo);
	}


	/**
	 * 保存专辑统计信息
	 *
	 * @param albumId  专辑ID
	 * @param statType 统计类型
	 * @param statNum  统计数值 0401-播放量 0402-订阅量 0403-购买量 0403-评论数'
	 */
	@Override
	public void saveAlbumInfoStat(Long albumId, String statType, int statNum) {
		AlbumStat albumStat = new AlbumStat();
		albumStat.setAlbumId(albumId);
		albumStat.setStatType(statType);
		albumStat.setStatNum(statNum);
		albumStatMapper.insert(albumStat);
	}

	/**
	 * 分页条件查询当前登录用户发布专辑
	 *
	 * @param pageParam      MP分页对象
	 * @param albumInfoQuery 查询条件
	 * @return
	 */
	@Override
	public Page<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> pageParam, AlbumInfoQuery albumInfoQuery) {
		return albumInfoMapper.findUserAlbumPage(pageParam, albumInfoQuery);
	}

	/**
	 * 删除专辑（包括标签关系、统计数值）
	 *
	 * @param id
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void removeAlbumInfo(Long id) {
		//1.根据专辑ID查询声音表判断该专辑下是否关联声音 如果存在 不允许删除
		Long count = trackInfoMapper.selectCount(
				new LambdaQueryWrapper<TrackInfo>()
						.eq(TrackInfo::getAlbumId, id)
		);
		if (count > 0) {
			throw new GuiguException(500, "该专辑下存在关联声音");
		}
		//2.删除专辑
		albumInfoMapper.deleteById(id);

		//3.删除专辑标签关系
		albumAttributeValueMapper.delete(
				new LambdaQueryWrapper<AlbumAttributeValue>()
						.eq(AlbumAttributeValue::getAlbumId, id)
		);

		//4.删除专辑统计信息
		albumStatMapper.delete(
				new LambdaQueryWrapper<AlbumStat>()
						.eq(AlbumStat::getAlbumId, id)
		);

		//5.删除专辑，发送MQ消息完成专辑下架（从ES中删除）
		rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);


	}


	/**
	 * 根据专辑ID查询专辑信息（包含专辑标签列表）
	 * 通过Redisson分布式锁解决缓存击穿问题
	 * 回显数据
	 *
	 * @param id 专辑ID
	 * @return
	 */
	@Override
	public AlbumInfo getAlbumInfo(Long id) {
/*		//1.根据ID查询专辑
		AlbumInfo albumInfo = albumInfoMapper.selectById(id);
		//2.根据专辑ID查询专辑标签关系列表
		if (albumInfo != null){
			List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(
					new LambdaQueryWrapper<AlbumAttributeValue>()
							.eq(AlbumAttributeValue::getAlbumId, id)
			);
			if (CollUtil.isNotEmpty(albumAttributeValues)) {
				albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
			}
		}
		return albumInfo;*/

		try {
			//1.优先从缓存Redis中获取业务数据
			//1.1 构建专辑业务数据Key 形式：前缀+专辑ID
			String dataKey = RedisConstant.ALBUM_INFO_PREFIX + id;
			//1.2 查询缓存中专辑信息
			AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
			if (albumInfo != null) {
				//2.如果命中缓存，直接返回业务数据即可（不需要查库）
				return albumInfo;
			}

			//3.如果未命中缓存，先获取分布式锁
			//3.1 构建当前业务数据锁的Key 形式：业务key+锁的后缀
			String lockKey = dataKey + RedisConstant.CACHE_LOCK_SUFFIX;
			//3.2 构建当前业务数据锁的对象
			RLock lock = redissonClient.getLock(lockKey);
			//3.3 尝试获取分布式锁 tryLock默认锁过期时间30s底层还有看门狗机制进行锁自动续期
			boolean flag = lock.tryLock();
			if (flag) {
				try {
					//4.获取分布锁锁成功，执行查询数据库，并设置缓存
					AlbumInfo albumInfoFromDB = this.getAlbumInfoFromDB(id);
					int ttl = RandomUtil.randomInt(100, 600);
					redisTemplate.opsForValue().set(dataKey, albumInfoFromDB, RedisConstant.ALBUM_TIMEOUT + ttl, TimeUnit.SECONDS);
					return albumInfoFromDB;
				} catch (Exception e) {
					throw new RuntimeException(e);
				} finally {
					//5.释放分布式锁
					lock.unlock();
				}
			} else {
				//6.获取分布式锁失败，进行自旋（自旋可能获取锁成功线程会将业务数据已经放入缓存）
				return this.getAlbumInfo(id);
			}
		} catch (Exception e) {
			log.error("[专辑服务]获取专辑信息锁服务异常异常：{},执行兜底处理方案：{}", e, "直接查询数据库");
			return this.getAlbumInfoFromDB(id);
		}
	}


	/**
	 * 抽取单独查询专辑数据业务方法
	 * 根据专辑ID查询专辑,包含标签列表(从数据库获取)
	 *
	 * @param id
	 * @return
	 */
	@Override
	@GuiGuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX)
	public AlbumInfo getAlbumInfoFromDB(Long id) {
		//1.根据专辑ID查询专辑信息
		AlbumInfo albumInfo = albumInfoMapper.selectById(id);

		//TODO 业务校验，校验专辑状态是否为下架状态，只有下架状态专辑才可以被修改

		//2.根据专辑ID查询专辑标签列表
		LambdaQueryWrapper<AlbumAttributeValue> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(AlbumAttributeValue::getAlbumId, id);
		List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(queryWrapper);
		if (CollectionUtil.isNotEmpty(albumAttributeValues)) {
			albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
		}
		return albumInfo;
	}


	/**
	 * 更新专辑信息
	 *
	 * @param id
	 * @param albumInfoVo
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {
		//1.更新专辑信息 状态：未审核
		AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
		albumInfo.setId(id);
		albumInfoMapper.updateById(albumInfo);

		//2.更新专辑标签关系
		//2.1 根据专辑ID删除原有标签关系
		albumAttributeValueMapper.delete(
					new LambdaQueryWrapper<AlbumAttributeValue>()
							.eq(AlbumAttributeValue::getAlbumId,id)
					);

		//2.2 从VO中获取提交专辑标签关系集合 再次批量保存
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
		if (CollUtil.isNotEmpty(albumAttributeValueVoList)){
			//为专辑标签关联专辑ID，"批量"新增专辑标签关系
/*			for (AlbumAttributeValueVo albumAttributeValueVo:albumAttributeValueVoList){
				AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
				albumAttributeValue.setAlbumId(id);

				albumAttributeValueMapper.insert(albumAttributeValue);
			}*/
			//2.3 封装专辑标签关系集合 关联专辑ID
			List<AlbumAttributeValue> attributeValueList = albumAttributeValueVoList
					.stream()
					.map(vo -> {
						AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(vo, AlbumAttributeValue.class);
						albumAttributeValue.setAlbumId(id);
						return albumAttributeValue;
					}).collect(Collectors.toList());
			//2.4 批量保存专辑标签关系
			albumAttributeValueService.saveBatch(attributeValueList);
		}

		//3.再次对内容进行审核
		String text = albumInfo.getAlbumTitle() + albumInfo.getAlbumIntro();
		String suggestion = auditService.auditText(text);
		if ("pass".equals(suggestion)) {
			albumInfo.setStatus(ALBUM_STATUS_PASS);
			//5.内容审核通过后发送MQ消息完成专辑上架（存入ES）
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
		} else if ("review".equals(suggestion)) {
			albumInfo.setStatus(ALBUM_STATUS_REVIEW);
		} else if ("block".equals(suggestion)) {
			albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
		}
		albumInfoMapper.updateById(albumInfo);
	}


	/**
	 * 获取当前用户全部专辑列表
	 * @param userId
	 * @return
	 */
	@Override
	public List<AlbumInfo> getUserAllAlbumList(Long userId) {
		//1.构建查询条件QueryWrapper对象
		LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
		//1.1 查询条件
		queryWrapper.eq(AlbumInfo::getUserId,userId);
		//1.2 排序
		queryWrapper.orderByDesc(AlbumInfo::getId);
		//1.3 限制记录数
		queryWrapper.last("LIMIT 200");
		//1.4 指定查询列
		queryWrapper.select(AlbumInfo::getId,AlbumInfo::getAlbumTitle);
		//2.执行列表查询
		return albumInfoMapper.selectList(queryWrapper);
	}




	/**
	 * 根据专辑ID查询专辑统计信息
	 * @param albumId
	 * @return
	 */
	@Override
	@GuiGuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX+":stat:")
	public AlbumStatVo getAlbumStatVo(Long albumId) {
		return albumInfoMapper.getAlbumStatVo(albumId);
	}


	/**
	 * 基于当前用户未购买声音数量动态渲染分集购买列表
	 *
	 * @param userId  用户ID
	 * @param trackId 选择购买声音ID，作为起始标准计算未购买声音数量
	 * @return [{name:"本集",price:0.1,trackCount:1},{name:"后10集",price:1,trackCount:10}..,{name:"全集",price:3.8,trackCount:38}]
	 */
	@Override
	public List<Map<String, Object>> findUserTrackPaidList(Long userId, Long trackId) {
		//1.根据选择购买声音ID查询声音记录，得到声音序号，得到专辑ID
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
		Integer orderNum = trackInfo.getOrderNum();
		Long albumId = trackInfo.getAlbumId();
		//2.根据"起始声音"需要+专辑ID查询声音表得到“未购买声音列表”，这里可能包含已购买声音
		List<TrackInfo> waitBuyTrackList = trackInfoMapper.selectList(
				new LambdaQueryWrapper<TrackInfo>()
						.eq(TrackInfo::getAlbumId, albumId)
						.eq(TrackInfo::getOrderNum, orderNum)
						.select(TrackInfo::getId)
		);

		//3.远程调用用户服务，得到用户已购声音ID列表，如果有值将已购买声音ID排除掉
		List<Long> userPaidTrackIdList =
				userFeignClient.findUserPaidTrackList(albumId).getData();
		if (CollUtil.isNotEmpty(userPaidTrackIdList)) {
			waitBuyTrackList = waitBuyTrackList
					.stream()
					.filter(t -> !userPaidTrackIdList.contains(t.getId()))
					.collect(Collectors.toList());
		}

		//4.基于未购买声音数量，动态封装分集购买列表
		int size = waitBuyTrackList.size();
		List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();

		//4.0 查询专辑记录得到单集价格
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		BigDecimal price = albumInfo.getPrice();
		//4.1 创建"本集"分集购买对象
		Map<String, Object> currMap = new HashMap<>();
		currMap.put("name","本集");
		currMap.put("price",price);
		currMap.put("trackCount",1);
		list.add(currMap);
		//4.2 创建"后i*10集"分集购买对象
		for (int i = 10; i < 50; i += 10) {
			Map<String, Object> map = new HashMap<>();
			if (i < size) {
				map.put("name","后" + i + "集");
				map.put("price",price.multiply(new BigDecimal(i)));
				map.put("trackCount",i);
				list.add(map);
			}else {
				map.put("name","全集(后" + size + "集)");
				map.put("price",price.multiply(new BigDecimal(size)));
				map.put("trackCount",size);
				list.add(map);
				break;
			}
		}
		return list;
	}

}
